# _*_ coding:utf-8 _*_
# 开发团队: Mavericks
# 开发人员：Maverick·Ming
# 开发IDE： PyCharm
#   项目名：HelloWorld
#   文件名：5.10 装饰器.py
# 开发时间：2021/3/25 15:16

"""
装饰器模式：java中AOP切面编程也是这种模式
"""


# 创建几个函数
def add(a, b):
    """
    求任意两数的和
    :param a:
    :param b:
    :return:
    """
    return a + b


def mul(a, b):
    """
    求任意两数的乘积
    :param a:
    :param b:
    :return:
    """
    return a * b


# 希望修改函数，能在计算前打印开始计算，计算结束以后打印计算完毕
# 原始方式：在函数内头部尾部用上print语句,但有两种问题：
#   1.如果需要修改的函数过多，修改起来效率低
#   2.不方便后期维护
#   3.这样违反了OCP开-闭原则
#       程序设计时，要求开发对程序的扩展，要关闭对程序的修改
#       开源程序修改容易出大问题

# 希望不修改程序的情况下进行拓展
def fn():
    print('我是fn函数，不能动！')


# 只需要根据现有的函数，来创建一个新的函数
def fn2():
    print('开始执行...')
    fn()
    print('执行结束！')


# fn2()


# 参上可知
def new_add(a, b):
    print('开始执行...')
    r = add(a, b)
    print('执行结束！')
    return r


def new_mul(a, b):
    print('开始执行...')
    r = mul(a, b)
    print('执行结束！')
    return r


# print(new_add(119,991))
# print(new_mul(4, 250))


# 上面的方式，已经可以在不修改源程序的情况下扩展代码
#   但是这种方式要求每拓展一个就要编写一个新的函数，实属麻烦
#   解决此问题，可以创建一个函数，让其自动帮助我们生产函数


# def begin_end(old):
#     """
#     对其他函数进行扩展，在其他函数执行前后进行打印
#     :return:
#     """
#     def new_function(a, b):
#         print('开始执行...')
#         # 调用被扩展的函数
#         result = old(a, b)
#         print('执行结束！')
#         return result
#     # 返回新函数
#     return new_function
#
#
# f = begin_end(add)  # 先对函数add进行改装，获取到new_add()给f()
# r = f(119, 911)     # 然后通过f()来调用改装后的函数
# print(r)            # 打印结果！
# 但是这样就把参数个数写死了，没法对任意参数的函数进行扩展，所以有了以下版本


def begin_end(old):
    """
    对其他函数进行扩展，在其他函数执行前后进行打印
    :return:
    """
    def new_function(*args, **kwargs):  # 这里入参，把你的参数装包成元组/字典
        print('开始执行...')
        # 调用被扩展的函数
        result = old(*args, **kwargs)  # 这里*args把元组解包成参数列表，**kwargs把字典解包成关键字列表
        print('执行结束！')
        return result
    # 返回新函数
    return new_function


# 以上就给新函数定义了不定长形参接收实参
# f = begin_end(add)  # 先对函数add进行改装，获取到new_add()给f()
# r = f(119, 911)     # 然后通过f()来调用改装后的函数
# print(r)            # 打印结果！
# f = begin_end(fn)  # 先对函数add进行改装，获取到new_add()给f()
# r = f()     # 然后通过f()来调用改装后的函数
# print(r)            # 打印结果！

# 如此一来无论是多少参数的函数均可修改
# 像begin_end()这种函数就是装饰器
#   通过装饰器，可以在不修改原来函数的情况下来对函数进行拓展
#   在开发中，我们都是通过装饰器来拓展函数的功能的
# 定义函数时，可以通过@装饰器，来使用指定的装饰器，来装饰当前函数
#   可以同时为一个函数同时指定多个装饰器

# 另外一种直接调用装饰器的方式
# @begin_end
def say_hello():
    print("我要say hello!")


say_hello()
# print(say_hello)
# 源本的<function say_hello at 0x000001F2C43AE318>
# 装饰后<function begin_end.<locals>.new_function at 0x00000279B33CE438>
